package com.key.win.base.auth;

import com.key.win.base.auth.detail.Authentication;
import com.key.win.base.exception.UserIllegalException;
import com.key.win.base.util.RedisUtil;
import com.key.win.base.util.SingleSoldierConstantUtils;
import com.key.win.business.chat.vo.ImGroupUserFullVo;
import com.key.win.system.model.SysDevice;
import com.key.win.system.vo.DataPermissionVo;
import com.key.win.system.vo.RefreshTokenVo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class AuthenticationUtil {

    private final static Logger logger = LoggerFactory.getLogger(AuthenticationUtil.class);

    private static final ThreadLocal<Authentication> userDetailsHolder = new ThreadLocal<Authentication>();

    private static RedisTemplate<String, Object> redisTemplate;

    private static int tokenExpires;

    private static int refreshTokenExpires;

    private static boolean isRefreshSelf;

    private static int webSocketDisconnectTokenOutTime;


    public static int getWebSocketDisconnectTokenOutTime() {
        return webSocketDisconnectTokenOutTime;
    }

    public static String getNickName() {
        Authentication loginUser = getAuthentication();
        if (loginUser != null) {
            return loginUser.getNickName();
        }
        return SingleSoldierConstantUtils.SYSTEM_ANONYMOUS_USER;
    }

    @Value("${spring.web.socket.disconnect.tokenOutTime:60}")
    public void setWebSocketDisconnectTokenOutTime(int webSocketDisconnectTokenOutTime) {
        AuthenticationUtil.webSocketDisconnectTokenOutTime = webSocketDisconnectTokenOutTime;
    }


    @Value("${spring.global.token.expires:86400}")
    public void setTokenExpires(int expires) {
        AuthenticationUtil.tokenExpires = expires;
    }

    @Value("${spring.global.refresh.token.expires:604800}")
    public void setRefreshTokenExpires(int expires) {
        AuthenticationUtil.refreshTokenExpires = expires;
    }

    @Value("${spring.global.refresh.token.isRefreshSelf:false}")
    public void setIsRefreshSelf(boolean b) {
        AuthenticationUtil.isRefreshSelf = b;
    }

    public static int getTokenExpires() {
        return tokenExpires;
    }

    public static int getRefreshTokenExpires() {
        return refreshTokenExpires;
    }

    public static boolean isIsRefreshSelf() {
        return isRefreshSelf;
    }

    @Autowired
    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        AuthenticationUtil.redisTemplate = redisTemplate;
    }


    /**
     * 获取登陆的 LoginAppUser
     *
     * @return
     */
    public static Authentication getAuthentication() {
        return userDetailsHolder.get();
    }

    /**
     * 设置当前用户Model
     *
     * @param sysUser
     */
    public static void setCurrentUser(Authentication sysUser) {
        userDetailsHolder.set(sysUser);
    }

    public static String getUserName() {
        Authentication loginUser = getAuthentication();
        if (loginUser != null) {
            return loginUser.getUserName();
        }
        return SingleSoldierConstantUtils.SYSTEM_ANONYMOUS_USER;
    }

    public static String getUserId() {
        Authentication loginUser = getAuthentication();
        if (loginUser != null) {
            return loginUser.getId();
        }
        return SingleSoldierConstantUtils.SYSTEM_ANONYMOUS_USER;
    }

    public static String getHeadImgUrl() {
        Authentication loginUser = getAuthentication();
        if (loginUser != null) {
            return loginUser.getHeadImgUrl();
        }
        return SingleSoldierConstantUtils.SYSTEM_ANONYMOUS_USER;
    }

    public static boolean isAnonymousUser() {
        Authentication loginUser = getAuthentication();
        if (loginUser == null) {
            return true;
        }
        return false;
    }

    public static String getToken() {
        Authentication loginUser = getAuthentication();
        if (loginUser != null) {
            return loginUser.getToken();
        }
        return "";
    }

    public static String getTokenException() {
        String token = getToken();
        if (StringUtils.isBlank(token)) {
            throw new UserIllegalException("token不存在！");
        }
        return token;
    }

    public static String getRefershTokenException() {
        String token = getRefershToken();
        if (StringUtils.isBlank(token)) {
            throw new UserIllegalException("refreshtoken不存在！");
        }
        return token;
    }

    private static String getRefershToken() {
        Authentication loginUser = getAuthentication();
        if (loginUser != null) {
            return loginUser.getRefreshToken();
        }
        return "";
    }

    public static void setAuthenticationToRedis(Authentication authentication) {
        setAuthenticationToRedis(authentication, tokenExpires);
    }

    public static void setAuthenticationToRedis(Authentication authentication, int tokenExpires) {
        redisTemplate.opsForValue().set(SingleSoldierConstantUtils.getRedisTokenKey(authentication.getToken()), authentication, tokenExpires, TimeUnit.SECONDS);
    }

    public static Authentication getAuthenticationToRedis() {
        return getAuthenticationToRedis(getTokenException());
    }

    public static Authentication getAuthenticationToRedis(String token) {
        return (Authentication) redisTemplate.opsForValue().get(SingleSoldierConstantUtils.getRedisTokenKey(token));
    }

    public static boolean deleteTokenToRedis(String token) {
        return redisTemplate.delete(SingleSoldierConstantUtils.getRedisTokenKey(token));
    }

    public static boolean logout() {
        deleteRefreshTokenToRedis(getRefershTokenException());
        return deleteTokenToRedis(getTokenException());
    }

    public static List<Authentication> getOnLineUser() throws Exception {
        List<Authentication> authenticationList = new ArrayList<>();
        Cursor<String> cursor = RedisUtil.scan(redisTemplate, SingleSoldierConstantUtils.REDIS_TOKEN_KEY_PREFIX + "*", 999);
        while (cursor.hasNext()) {
            Authentication authentication = (Authentication) redisTemplate.opsForValue().get(cursor.next());
            authenticationList.add(authentication);
        }
        cursor.close();
        return authenticationList;
    }

    public static Map<String, Authentication> getOnLineUserToMap() {
        try {
            return AuthenticationUtil.getOnLineUser().stream().collect(Collectors.toMap(Authentication::getId, a -> a, (k1, k2) -> k1));
        } catch (Exception e) {
            logger.error("getOnLineUserToMap is error:{}!", e.getMessage(), e);
            return null;
        }
    }

    public static Authentication getOnLineUserByUserId(String userId) {
        Map<String, Authentication> onLineUserToMap = AuthenticationUtil.getOnLineUserToMap();
        if (CollectionUtils.isEmpty(onLineUserToMap)) {
            return null;
        }
        return onLineUserToMap.get(userId);
    }

    public static RefreshTokenVo getRefreshTokenToRedis(String refreshToken) {
        return (RefreshTokenVo) redisTemplate.opsForValue().get(SingleSoldierConstantUtils.getRedisRefreshTokenKey(refreshToken));
    }

    public static void setRefreshTokenToRedis(String refreshToken) {
        RefreshTokenVo refreshTokenVo = new RefreshTokenVo();
        refreshTokenVo.setToken(getToken());
        refreshTokenVo.setUserName(getUserName());
        setRefreshTokenToRedis(refreshToken, refreshTokenVo);
    }

    public static void setRefreshTokenToRedis(String refreshToken, Authentication authentication) {
        RefreshTokenVo refreshTokenVo = new RefreshTokenVo();
        refreshTokenVo.setToken(authentication.getToken());
        refreshTokenVo.setUserName(authentication.getUserName());
        setRefreshTokenToRedis(refreshToken, refreshTokenVo);
    }

    public static void setRefreshTokenToRedis(String refreshToken, RefreshTokenVo refreshTokenVo) {
        redisTemplate.opsForValue().set(SingleSoldierConstantUtils.getRedisRefreshTokenKey(refreshToken), refreshTokenVo, refreshTokenExpires, TimeUnit.SECONDS);
    }

    public static void deleteRefreshTokenToRedis(String refreshToken) {
        redisTemplate.delete(SingleSoldierConstantUtils.getRedisRefreshTokenKey(refreshToken));
    }

    public static DataPermissionVo getDataPermissionVo() {
        Authentication loginUser = getAuthentication();
        if (loginUser != null) {
            return loginUser.getDataPermissionVo();
        }
        return null;
    }

    public static Set<String> getAllDeviceIds() {
        DataPermissionVo dataPermissionVo = getDataPermissionVo();
        if (dataPermissionVo != null) {
            Map<String, SysDevice> devices = dataPermissionVo.getDevices();
            if (CollectionUtils.isEmpty(devices)) {
                return devices.keySet();
            }
        }
        return new HashSet<>();
    }

    public static Set<String> getDeviceIdsByUserIds(Set<String> userIds) {
        DataPermissionVo dataPermissionVo = getDataPermissionVo();
        Set<String> deviceIds = new HashSet<>();
        if (dataPermissionVo != null) {
            Map<String, Map<String, SysDevice>> userIdSysDeviceMap = dataPermissionVo.getUserIdSysDeviceMap();
            if (CollectionUtils.isEmpty(userIdSysDeviceMap)) {
                for (String userId : userIds) {
                    Map<String, SysDevice> stringSysDeviceMap = userIdSysDeviceMap.get(userId);
                    deviceIds.addAll(stringSysDeviceMap.values().stream().map(SysDevice::getId).collect(Collectors.toSet()));
                }
            }
        }
        return deviceIds;
    }

    public static Set<String> getAllGruopIds() {
        DataPermissionVo dataPermissionVo = getDataPermissionVo();
        if (dataPermissionVo != null) {
            Map<String, ImGroupUserFullVo> groups = dataPermissionVo.getGroups();
            if (CollectionUtils.isEmpty(groups)) {
                return groups.keySet();
            }
        }
        return new HashSet<>();
    }

    public static Set<String> getGropIdsByUserIds(Set<String> userIds) {
        DataPermissionVo dataPermissionVo = getDataPermissionVo();
        Set<String> deviceIds = new HashSet<>();
        if (dataPermissionVo != null) {
            Map<String, Map<String, ImGroupUserFullVo>> userIdGropMap = dataPermissionVo.getUserIdGropMap();
            if (CollectionUtils.isEmpty(userIdGropMap)) {
                for (String userId : userIds) {
                    Map<String, ImGroupUserFullVo> stringSysDeviceMap = userIdGropMap.get(userId);
                    deviceIds.addAll(stringSysDeviceMap.values().stream().map(ImGroupUserFullVo::getGroupId).collect(Collectors.toSet()));
                }
            }
        }
        return deviceIds;
    }
}
